package jmine.tec.report.impl.table;

import jmine.tec.report.api.RendererTarget;

/**
 * Classe wrappers de elementos de relatórios que dão uma visão mais protegida do mapa de parâmetros de renderização.
 * 
 * @author lundberg
 */
public class RendererParameterInterface {

    // Parâmetros permitidos
    /**
     * Parâmetro que afirma que a célula é um título, e deve apresentar o estilo adequado.<br/>
     * O valor é irrelevante, desde que não seja nulo.
     */
    public static final String PARAM_TITLE = "isTitle";

    /**
     * Parâmetro que, quando preenchido, indica que a célula percence a um cabeçalho ou rodapé de um grupo de registros, e o valor
     * representa um inteiro com o nível do subgrupo. O subgrupo mais externo é "0", o seguinte é "1" e assim por diante.
     */
    public static final String PARAM_GROUP_INDEX = "groupIndex";

    /**
     * Parâmetro que afirma que a célula pertence a um cabeçalho, e deve apresentar o estilo adequado.<br/>
     * O valor é irrelevante, desde que não seja nulo.
     */
    public static final String PARAM_HEADER = "isHeader";

    /**
     * Retorna o rendererType das células que compõe a coluna que tem esta célula como cabeçalho.
     */
    public static final String PARAM_COLUMN_TYPE = "columnType";

    /**
     * Parâmetro que define o nome da coluna do campo na query para que seja possível ordenar a coluna clicando<br/>
     * no cabeçalho (JSF). Caso seja definido, o campo será usado por um SortingActionListener. Aplicável apenas<br/>
     * em células que são cabeçalhos de tabelas, quando representadas em JSF.
     * 
     * @see bancosys.tec.web.jsf.component.sorting.SortingActionListener
     */
    public static final String PARAM_SORT_COLUMN = "sortColumn";

    /**
     * Parâmetro que, quando preenchido, define que a coluna deve ser a ordenação padrão da consulta.<br/>
     * Tem como pre-requisito que PARAM_SORT_COLUMN e PARAM_HEADER sejam preenchidos.
     */
    public static final String PARAM_DEFAULT_SORT_COLUMN = "defaultSortColumn";

    /**
     * Parâmetro que afirma que a célula pertence a um rodapé, e deve apresentar o estilo adequado.<br/>
     * O valor é irrelevante, desde que não seja nulo.
     */
    public static final String PARAM_FOOTER = "isFooter";

    /**
     * Parâmetro que afirma quantas casas decimais devem ser apresentadas.<br/>
     * Aplicável para campo do tipo "number".
     */
    public static final String PARAM_DECIMALS = "decimals";

    /**
     * Parâmetro que afirma que a coluna não deve ser renderizada. O valor deve ficar disponível apenas programaticamente. <br/>
     * O valor é uma string que pode ser convertida de boolean.
     */
    public static final String PARAM_HIDDEN = "isHidden";

    /**
     * Parâmetro que contém a largura relativa de uma coluna. Por padrão a largura relativa de uma coluna deve ser 1.
     */
    public static final String PARAM_RELATIVE_WIDTH = "relativeWidth";

    /**
     * Parâmetro que contém o formado de data desejado.
     */
    public static final String PARAM_DATE_PATTERN = "datePattern";

    private final RendererTarget target;

    /**
     * Construtor
     * 
     * @param target target
     */
    public RendererParameterInterface(RendererTarget target) {
        this.target = target;
    }

    /**
     * Verifica se o elemento é um título.
     * 
     * @return boolean
     */
    public boolean getIsTitle() {
        return this.getBoolean(PARAM_TITLE);
    }

    /**
     * Define se o elemento é um título
     * 
     * @param isTitle isTitle
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setIsTitle(Boolean isTitle) {
        this.setBoolean(PARAM_TITLE, isTitle);
        return this;
    }

    /**
     * Verifica se o elemento é um cabeçalho.
     * 
     * @return boolean
     */
    public boolean getIsHeader() {
        return this.getBoolean(PARAM_HEADER);
    }

    /**
     * Define se o elemento é um cabeçalho
     * 
     * @param isHeader isHeader
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setIsHeader(Boolean isHeader) {
        this.setBoolean(PARAM_HEADER, isHeader);
        return this;
    }

    /**
     * Define o tipo da coluna que tem esta célula como cabeçalho.
     * 
     * @param columnType columnType
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setColumnType(String columnType) {
        this.target.getParameters().put(PARAM_COLUMN_TYPE, columnType);
        return this;
    }

    /**
     * Retorna o tipo da coluna, caso tenha sido definido.
     * 
     * @return columnType
     */
    public String getColumnType() {
        return this.target.getParameters().getString(PARAM_COLUMN_TYPE);
    }

    /**
     * Verifica se a coluna é cabeçalho de um campo number
     * 
     * @return boolean
     */
    public boolean isNumberColumnHeader() {
        return this.getIsHeader() && ReportTableBuilder.TYPE_NUMBER.equals(this.getColumnType());
    }
    
    /**
     * Verifica se a coluna é cabeçalho de um campo porcentagem
     * 
     * @return boolean
     */
    public boolean isPorcentagemColumnHeader() {
        return this.getIsHeader() && ReportTableBuilder.TYPE_PORCENTAGEM.equals(this.getColumnType());
    }

    /**
     * Verifica se a coluna é cabeçalho de um campo date
     * 
     * @return boolean
     */
    public boolean isDateColumnHeader() {
        return this.getIsHeader() && ReportTableBuilder.TYPE_DATE.equals(this.getColumnType());
    }

    /**
     * Verifica se o elemento é um rodapé.
     * 
     * @return boolean
     */
    public boolean getIsFooter() {
        return this.getBoolean(PARAM_FOOTER);
    }

    /**
     * Define se o elemento é um rodapé
     * 
     * @param isFooter isFooter
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setIsFooter(Boolean isFooter) {
        this.setBoolean(PARAM_FOOTER, isFooter);
        return this;
    }

    /**
     * Verifica se o elemento é uma linha de um grupo
     * 
     * @return boolean
     */
    public boolean isGroup() {
        return this.isDefined(PARAM_GROUP_INDEX);
    }

    /**
     * Retorna o índice do grupo, caso seja um grupo.
     * 
     * @return Integer
     */
    public Integer getGroupIndex() {
        return this.getInteger(PARAM_GROUP_INDEX);
    }

    /**
     * Define o índice do grupo
     * 
     * @param groupIndex groupIndex
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setGroupIndex(Integer groupIndex) {
        this.setInteger(PARAM_GROUP_INDEX, groupIndex);
        return this;
    }

    /**
     * Verifica se é cabeçalho de uma coluna com ordenação.
     * 
     * @return boolean boolean
     */
    public boolean isSortColumn() {
        return this.isDefined(PARAM_SORT_COLUMN);
    }

    /**
     * Retorna o nome da coluna ordenada
     * 
     * @return String
     */
    public String getSortColumn() {
        return this.target.getParameters().getString(PARAM_SORT_COLUMN);
    }

    /**
     * Define o nome da coluna ordenada
     * 
     * @param sortColumn sortColumn
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setSortColumn(String sortColumn) {
        this.target.getParameters().put(PARAM_SORT_COLUMN, sortColumn);
        return this;
    }

    /**
     * Verifica se está definida uma ordenação default
     * 
     * @return boolean
     */
    public boolean isDefaultSortColumn() {
        return this.isDefined(PARAM_DEFAULT_SORT_COLUMN);
    }

    /**
     * Retorna se a ordenação default é ascendente, falso caso não definida.
     * 
     * @return boolean
     */
    public boolean getDefaultSort() {
        return this.getBoolean(PARAM_DEFAULT_SORT_COLUMN);
    }

    /**
     * Define se a ordenação default é ascendente (true), descendente (false) ou não definida (null)
     * 
     * @param asc asc
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setDefaultSort(Boolean asc) {
        this.setBoolean(PARAM_DEFAULT_SORT_COLUMN, asc);
        return this;
    }

    /**
     * Retorna o número de casas decimais que o campo numérico deve mostrar
     * 
     * @return decimals
     */
    public Integer getDecimals() {
        return this.getInteger(PARAM_DECIMALS);
    }

    /**
     * Define o número de casas decimais que o campo numérico deve mostrar.
     * 
     * @param decimals decimals
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setDecimals(Integer decimals) {
        this.setInteger(PARAM_DECIMALS, decimals);
        return this;
    }

    /**
     * Verifica se a coluna deve ser escondida, não renderizada.
     * 
     * @return hidden
     */
    public boolean isHidden() {
        return this.getBoolean(PARAM_HIDDEN);
    }

    /**
     * Define se a coluna deve ser escondida, não renderizada.
     * 
     * @param hidden hidden
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setHidden(boolean hidden) {
        this.setBoolean(PARAM_HIDDEN, hidden);
        return this;
    }

    /**
     * Obtém a largura relativa da coluna.
     * 
     * @return relativeWidth
     */
    public Double getRelativeWidth() {
        Double relativeWidth = this.getDouble(PARAM_RELATIVE_WIDTH);
        return relativeWidth == null ? 1.0 : relativeWidth;
    }

    /**
     * Define a largura relativa
     * 
     * @param relativeWidth relativeWidth
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setRelativeWidth(Double relativeWidth) {
        this.setDouble(PARAM_RELATIVE_WIDTH, relativeWidth);
        return this;
    }

    /**
     * Obtém o padrão de texto para datas.
     * 
     * @return o padrão
     */
    public String getDatePattern() {
        return this.getString(PARAM_DATE_PATTERN);
    }

    /**
     * Define o padrão de texto para datas.
     * 
     * @param datePattern o padrão
     * @return RendererParameterInterface, para method chaining
     */
    public RendererParameterInterface setDatePattern(String datePattern) {
        this.setString(PARAM_DATE_PATTERN, datePattern);
        return this;
    }

    /**
     * Auxiliar, retorna o valor do campo com a chave.
     * 
     * @param key key
     * @return Integer
     */
    private Integer getInteger(String key) {
        String value = this.target.getParameters().getString(key);
        return value == null ? null : Integer.parseInt(value);
    }

    /**
     * Auxiliar, define o valor do campo com a chave.
     * 
     * @param key key
     * @param value value
     */
    private void setInteger(String key, Integer value) {
        this.target.getParameters().put(key, value == null ? null : value.toString());
    }

    /**
     * Auxiliar, define o valor do campo com a chave.
     * 
     * @param key key
     * @param value value
     */
    private void setBoolean(String key, Boolean value) {
        this.target.getParameters().put(key, value == null ? null : value.toString());
    }

    /**
     * Auxiliar, retorna o valor do campo com a chave.
     * 
     * @param key key
     * @return Integer
     */
    private boolean getBoolean(String key) {
        String value = this.target.getParameters().getString(key);
        return value != null && Boolean.parseBoolean(value);
    }

    /**
     * Auxiliar, define o valor do campo com a chave.
     * 
     * @param key key
     * @param value value
     */
    private void setDouble(String key, Double value) {
        this.target.getParameters().put(key, value == null ? null : value.toString());
    }

    /**
     * Auxiliar, retorna o valor do campo com a chave.
     * 
     * @param key key
     * @return Double
     */
    private Double getDouble(String key) {
        String value = this.target.getParameters().getString(key);
        return value == null ? null : Double.parseDouble(value);
    }

    /**
     * Auxiliar, define o valor do campo de texto com a chave.
     * 
     * @param key key
     * @param value value
     */
    private void setString(String key, String value) {
        this.target.getParameters().put(key, value);
    }

    /**
     * Auxiliar, retorna o valor do campo de texto com a chave.
     * 
     * @param key key
     * @return Double
     */
    private String getString(String key) {
        return this.target.getParameters().getString(key);
    }

    /**
     * Verifica se o campo foi definido.
     * 
     * @param key key
     * @return boolean
     */
    private boolean isDefined(String key) {
        return this.target.getParameters().getString(key) != null;
    }
}
